﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace TriplesProblems
{
    public class Triple : IComparable<Triple>, ICloneable, IEnumerable<int>
    {
        private int[] values = new int[3];

        public int this[int index]
        {
            get
            {
                if (this == null)
                {
                    throw new NullReferenceException();
                }
                if (index < 0 || index > 2)
                {
                    throw new IndexOutOfRangeException();
                }
                return this.values[index];
            }
            set
            {
                if (this == null)
                {
                    throw new NullReferenceException();
                }
                if (index < 0 || index > 2)
                {
                    throw new IndexOutOfRangeException();
                }
                this.values[index] = value;
            }
        }

        public Triple()
        {
        }

        public Triple(int a, int b, int c)
        {
            this[0] = a;
            this[1] = b;
            this[2] = c;
        }

        public int GetValuesSum()
        {
            int sum = 0;
            foreach (int value in this.values)
            {
                sum += value;
            }
            return sum;
        }

        public static Triple operator +(Triple t1, Triple t2)
        {
            Triple sum = new Triple();
            for (int i = 0; i < 3; i++)
            {
                sum[i] = t1[i] + t2[i];
            }
            return sum;
        }

        public static Triple operator -(Triple t1, Triple t2)
        {
            Triple sum = new Triple();
            for (int i = 0; i < 3; i++)
            {
                sum[i] = t1[i] - t2[i];
            }
            return sum;
        }

        public static Triple operator ++(Triple t)
        {
            for (int i = 0; i < 3; i++)
            {
                t[i]++;
            }
            return t;
        }

        public int CompareTo(Triple other)
        {
            if (this == null || other == null)
            {
                throw new NullReferenceException();
            }
            int thisSum = this.GetValuesSum();
            int otherSum = other.GetValuesSum();
            if (thisSum < otherSum)
            {
                return -1;
            }
            else if (thisSum > otherSum)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }

        public object Clone()
        {
            Triple clone = new Triple(this[0], this[1], this[2]);
            return clone as object;
        }

        public IEnumerator<int> GetEnumerator()
        {
            foreach (int value in this.values)
            {
                yield return value;
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public override string ToString()
        {
            if (this == null)
            {
                return "null";
            }
            StringBuilder asString = new StringBuilder();
            asString.Append("(");
            foreach (var member in this.values)
            {
                asString.Append(member);
                asString.Append(", ");
            }
            asString.Remove(asString.Length - 2, 2);
            asString.Append(")");
            return asString.ToString();
        }

        public static Triple[] ExtractTriplesFromTextFile(string path)
        {
            List<Triple> triples = new List<Triple>();
            string integerPattern = @"((-?[1-9]+\d*)|0)";
            string tripplePattern = @"(?<match>\(" + integerPattern +@",\s*" + integerPattern + @",\s*" + integerPattern + @"\))";
            Regex tripleExtractor = new Regex(tripplePattern);
            Regex integerExtractor = new Regex(integerPattern);
            MatchCollection tripleMatches;
            MatchCollection integerMatches;
            StreamReader reader = new StreamReader(path);
            using (reader)
            {
                StringBuilder text = new StringBuilder();
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    text.Append(line);
                    tripleMatches = tripleExtractor.Matches(text.ToString());
                    int toRemoveLength = 0;
                    if (tripleMatches.Count != 0)
                    {
                        //TODO: add optimization to reduce text length
                        //check if partial triple patterns are present in the text
                        //Partial Triple Patterns -> @"\(", @"\(integerPattern", @"\(integerPattern,\s*", ..., @"\(integerPattern,\s*integerPattern,\s*integerPattern" end
                        //if so text.Remove(0, firstMatch.Index - 1)
                        //else text.Clear();
                        toRemoveLength = tripleMatches[tripleMatches.Count - 1].Index + tripleMatches[tripleMatches.Count - 1].Length;
                    }
                    foreach(Match match in tripleMatches)
                    {
                        integerMatches = integerExtractor.Matches(match.Groups["match"].ToString());
                        Triple triple = new Triple();
                        
                        for (int i = 0; i < 3; i++)
                        {
                            int currentInt = Convert.ToInt32(integerMatches[i].ToString());
                            triple[i] = currentInt;
                        }
                        triples.Add(triple);
                    }
                    text.Remove(0, toRemoveLength);
                }
            }
            return triples.ToArray();
        }
    }
}
